home *** CD-ROM | disk | FTP | other *** search
/ Complete Linux / Complete Linux.iso / docs / apps / database / postgres / postgre4.z / postgre4 / src / planner / path / xfunc.c < prev   
Encoding:
C/C++ Source or Header  |  1993-05-05  |  36.3 KB  |  1,241 lines

  1. /* $Header: /private/postgres/src/planner/path/RCS/xfunc.c,v 1.28 1992/08/10 21:43:43 mer Exp $ */
  2.  
  3. /*     
  4. **      FILE
  5. **         xfunc
  6. **     
  7. **      DESCRIPTION
  8. **         Routines to handle expensive function optimization
  9. **     
  10. */
  11.  
  12.  
  13. /*     
  14. **      EXPORTS
  15. **              xfunc_trypullup
  16. **              xfunc_get_path_cost
  17. **              xfunc_clause_compare
  18. **              xfunc_disjunct_sort
  19. */
  20. #include <math.h>      /* for HUGE_VAL */
  21. #include <strings.h>
  22.  
  23. #include "nodes/pg_lisp.h"
  24. #include "nodes/nodes.h"
  25. #include "nodes/primnodes.h"
  26. #include "nodes/primnodes.a.h"
  27. #include "nodes/relation.h"
  28. #include "utils/log.h"
  29. #include "utils/palloc.h"
  30. #include "catalog/pg_proc.h"
  31. #include "catalog/pg_type.h"
  32. #include "catalog/syscache.h"
  33. #include "catalog/pg_language.h"
  34. #include "planner/xfunc.h"
  35. #include "planner/clausesel.h"
  36. #include "planner/relnode.h"
  37. #include "planner/internal.h"
  38. #include "planner/costsize.h"
  39. #include "parser/parse.h"
  40. #include "planner/keys.h"
  41. #include "planner/tlist.h"
  42. #include "lib/lispsort.h"
  43. #include "lib/copyfuncs.h"
  44. #include "access/heapam.h"
  45. #include "tcop/dest.h"
  46.  
  47. #define ever ; 1 ;
  48. #define ABS(x) (((x) > 1) ? (x) : (-(x)))
  49. #ifdef sequent
  50. #define HUGE_VAL    1.8e+308
  51. #endif
  52. #ifdef linux
  53. #undef HUGE_VAL
  54. #define HUGE_VAL MAXFLOAT
  55. #endif
  56.  
  57. /*
  58. ** xfunc_trypullup --
  59. **    Main entry point to expensive function optimization.
  60. ** Given a relation, check each of its paths and see if it should 
  61. ** pullup clauses from its inner and outer.
  62. */
  63.  
  64. void xfunc_trypullup(rel)
  65.      Rel rel;
  66. {
  67.     LispValue y;            /* list ptr */
  68.     CInfo maxcinfo;         /* The CInfo to pull up, as calculated by
  69.                    xfunc_shouldpull() */
  70.     JoinPath curpath;       /* current path in list */
  71.     int progress;           /* has progress been made this time through? */
  72.     LispValue form_relid();
  73.     int clausetype;
  74.  
  75.     do {
  76.     progress = false;  /* no progress yet in this iteration */
  77.     foreach(y, get_pathlist(rel))
  78.      {
  79.          curpath = (JoinPath)CAR(y);
  80.      
  81.          /*
  82.           ** for each operand, attempt to pullup predicates until first 
  83.           ** failure.
  84.           */
  85.          for(ever)
  86.           {
  87.           /* No, the following should NOT be '=='  !! */
  88.           if (clausetype = 
  89.               xfunc_shouldpull((Path)get_innerjoinpath(curpath),
  90.                        curpath, INNER, &maxcinfo))
  91.            {
  92.                xfunc_pullup((Path)get_innerjoinpath(curpath),
  93.                     curpath, maxcinfo, INNER, clausetype);
  94.                progress = true;
  95.            }
  96.           else break;
  97.           }
  98.          for(ever)
  99.           {
  100.           /* No, the following should NOT be '=='  !! */
  101.           if (clausetype = 
  102.               xfunc_shouldpull((Path)get_outerjoinpath(curpath), 
  103.                        curpath, OUTER, &maxcinfo))
  104.            {
  105.                xfunc_pullup((Path)get_outerjoinpath(curpath),
  106.                     curpath, maxcinfo, OUTER, clausetype);
  107.                progress = true;
  108.            }
  109.           else break;
  110.           }
  111.      }
  112.     } while(progress);
  113. }
  114.  
  115. /*
  116. ** xfunc_shouldpull --
  117. **    find clause with most expensive measure, and decide whether to pull it up
  118. ** from child to parent.  Currently we only pullup secondary join clauses
  119. ** that are in the pathclauseinfo.  Secondary hash and sort clauses are
  120. ** left where they are.
  121. **
  122. ** Returns:  0 if nothing left to pullup
  123. **           XFUNC_LOCPRD if a local predicate is to be pulled up
  124. **           XFUNC_JOINPRD if a secondary join predicate is to be pulled up
  125. */
  126. int xfunc_shouldpull(childpath, parentpath, whichchild, maxcinfopt)
  127.      Path childpath;
  128.      JoinPath parentpath;
  129.      int whichchild;     /* whether child is INNER or OUTER of join */
  130.      CInfo *maxcinfopt;  /* Out: pointer to clause to pullup */
  131. {
  132.     LispValue clauselist, tmplist; /* lists of clauses */
  133.     CInfo maxcinfo;        /* clause to pullup */
  134.     LispValue primjoinclause    /* primary join clause */
  135.       = xfunc_primary_join(parentpath);
  136.     Cost tmpmeasure, maxmeasure = 0; /* measures of clauses */
  137.     Cost joinselec = 0;    /* selectivity of the join predicate */
  138.     Cost joincost = 0;    /* join cost + primjoinclause cost */
  139.     int retval = XFUNC_LOCPRD;
  140.  
  141.     clauselist = get_locclauseinfo(childpath);
  142.  
  143.     if (clauselist != LispNil)
  144.      {
  145.      /* find local predicate with maximum measure */
  146.      for (tmplist = clauselist,
  147.           maxcinfo = (CInfo) CAR(tmplist),
  148.           maxmeasure = xfunc_measure(get_clause(maxcinfo));
  149.           tmplist != LispNil;
  150.           tmplist = CDR(tmplist))
  151.        if ((tmpmeasure = xfunc_measure(get_clause((CInfo) CAR(tmplist)))) 
  152.            > maxmeasure)
  153.         {
  154.         maxcinfo = (CInfo) CAR(tmplist);
  155.         maxmeasure = tmpmeasure;
  156.         }
  157.      }
  158.  
  159.      
  160.     /* 
  161.     ** If child is a join path, and there are multiple join clauses,
  162.     ** see if any join clause has even higher measure than the highest
  163.     ** local predicate 
  164.     */
  165.     if (length((get_parent(childpath))->relids) > 1
  166.     && xfunc_num_join_clauses((JoinPath)childpath) > 1)
  167.       for (tmplist = get_pathclauseinfo((JoinPath)childpath);
  168.        tmplist != LispNil;
  169.        tmplist = CDR(tmplist))
  170.     if ((tmpmeasure = xfunc_measure(get_clause((CInfo) CAR(tmplist)))) 
  171.         > maxmeasure)
  172.      {
  173.          maxcinfo = (CInfo) CAR(tmplist);
  174.          maxmeasure = tmpmeasure;
  175.          retval = XFUNC_JOINPRD;
  176.      }
  177.  
  178.     if (maxmeasure == 0)    /* no expensive clauses */
  179.       return(0);
  180.  
  181.     /*
  182.     ** Pullup over join if clause is higher measure than join.
  183.     ** Note that the cost of a secondary join clause is only what's
  184.     ** calculated by xfunc_expense(), since the actual joining 
  185.     ** (i.e. the usual path_cost) is paid for by the primary join clause.
  186.     ** The cost of the join clause is the cost of the primary join clause
  187.     ** plus the expense_per_tuple of whichchild for the join method.
  188.     */
  189.     if (primjoinclause != LispNil)
  190.      {
  191.      joinselec = compute_clause_selec(primjoinclause, LispNil);
  192.  
  193.      /*
  194.      ** the following block of code assumes no function caching
  195.      ** Modify/Delete it when function caching gets implemented.
  196.      */
  197.      if (whichchild = INNER)
  198.        joinselec *= 
  199.          get_tuples(get_parent((Path)get_outerjoinpath(parentpath)));
  200.      else
  201.        joinselec *=
  202.         get_tuples(get_parent((Path)get_innerjoinpath(parentpath)));
  203.      
  204.  
  205.      joincost = xfunc_expense_per_tuple(parentpath, whichchild) 
  206.        + xfunc_expense(primjoinclause);
  207.      
  208.      if ((joincost != 0 &&
  209.           xfunc_measure(get_clause(maxcinfo)) > 
  210.           ((joinselec - 1.0) / joincost))
  211.          || (joincost == 0 && joinselec < 1))
  212.       {
  213.           *maxcinfopt = maxcinfo;
  214.           return(retval);
  215.       }
  216.      else            /* drop through */;
  217.      }
  218.  
  219.     return(0);
  220.  
  221. }
  222.  
  223.  
  224. /*
  225. ** xfunc_pullup --
  226. **    move clause from child pathnode to parent pathnode.   This operation 
  227. ** makes the child pathnode produce a larger relation than it used to.
  228. ** This means that we must construct a new Rel just for the childpath,
  229. ** although this Rel will not be added to the list of Rels to be joined up
  230. ** in the query; it's merely a parent for the new childpath.
  231. **    We also have to fix up the path costs of the child and parent.
  232. */
  233. void xfunc_pullup(childpath, parentpath, cinfo, whichchild, clausetype)
  234.      Path childpath;
  235.      JoinPath parentpath;
  236.      CInfo cinfo;         /* clause to pull up */
  237.      int whichchild;      /* whether child is INNER or OUTER of join */
  238.      int clausetype;      /* whether clause to pull is join or local */
  239. {
  240.     Path newkid;
  241.     Rel newrel;
  242.     Cost pulled_selec;
  243.     Cost cost;
  244.     CInfo newinfo;
  245.  
  246.     /* remove clause from childpath */
  247.     if (clausetype == XFUNC_LOCPRD)
  248.      {
  249.      newkid = (Path)CopyObject(childpath);
  250.      set_locclauseinfo(newkid, 
  251.                xfunc_LispRemove((LispValue)cinfo, 
  252.                         (List)get_locclauseinfo(newkid)));
  253.      }
  254.     else
  255.      {
  256.      newkid = (Path)CopyObject(childpath);
  257.      set_pathclauseinfo
  258.        ((JoinPath)newkid,
  259.         xfunc_LispRemove((LispValue)cinfo, 
  260.                  (List)get_pathclauseinfo((JoinPath)newkid)));
  261.      }
  262.  
  263.     /*
  264.     ** give the new child path its own Rel node that reflects the
  265.     ** lack of the pulled-up predicate
  266.     */
  267.     pulled_selec = compute_clause_selec(get_clause(cinfo), LispNil);
  268.     xfunc_copyrel(get_parent(newkid), &newrel);
  269.     set_parent(newkid, newrel);
  270.     set_pathlist(newrel, lispCons((LispValue)newkid, LispNil));
  271.     set_unorderedpath(newrel, (PathPtr)newkid);
  272.     set_cheapestpath(newrel, (PathPtr)newkid);
  273.     set_tuples(newrel, 
  274.            (Count)((Cost)get_tuples(get_parent(childpath)) / pulled_selec));
  275.     set_pages(newrel, 
  276.           (Count)((Cost)get_pages(get_parent(childpath)) / pulled_selec));
  277.     
  278.     /* 
  279.     ** fix up path cost of newkid.  To do this we subtract away all the
  280.     ** xfunc_costs of childpath, then recompute the xfunc_costs of newkid
  281.     */
  282.     cost = get_path_cost(newkid) - xfunc_get_path_cost(childpath);
  283.     Assert(cost >= 0);
  284.     set_path_cost(newkid, cost);
  285.     cost = get_path_cost(newkid) + xfunc_get_path_cost(newkid);
  286.     set_path_cost(newkid, cost);
  287.  
  288.     /*
  289.     ** We copy the cinfo, since it may appear in other plans, and we're going
  290.     ** to munge it.  -- JMH, 7/22/92
  291.     */
  292.     newinfo = (CInfo)CopyObject(cinfo);
  293.  
  294.     /* 
  295.     ** Fix all vars in the clause 
  296.     ** to point to the right varno and varattno in parentpath 
  297.     */
  298.     xfunc_fixvars(get_clause(newinfo), newrel, whichchild);
  299.  
  300.     /*  add clause to parentpath, and fix up its cost. */
  301.     set_locclauseinfo(parentpath, 
  302.               lispCons((LispValue)newinfo, 
  303.                    (LispValue)get_locclauseinfo(parentpath)));
  304.     /* put new childpath into the path tree */
  305.     if (whichchild == INNER)
  306.      {
  307.      set_innerjoinpath(parentpath, (pathPtr)newkid);
  308.      }
  309.     else
  310.      {
  311.      set_outerjoinpath(parentpath, (pathPtr)newkid);
  312.      }
  313.  
  314.     /* 
  315.     ** recompute parentpath cost from scratch -- the cost
  316.     ** of the join method has changed
  317.     */
  318.     cost = xfunc_total_path_cost(parentpath);
  319.     set_path_cost(parentpath, cost);
  320. }
  321.  
  322. /*
  323. ** calculate -(1-selectivity)/cost. 
  324. */
  325. Cost xfunc_measure(clause)
  326.      LispValue clause;
  327. {
  328.     Cost selec = compute_clause_selec(clause, LispNil); 
  329.     Cost cost = xfunc_expense(clause);
  330.  
  331.     if (cost == 0) 
  332.       if (selec > 1) return(HUGE_VAL);
  333.     else return(-(HUGE_VAL));
  334.     return((selec - 1)/cost);
  335. }
  336.  
  337. /*
  338. ** Recursively find the per-tuple expense of a clause.  See
  339. ** xfunc_func_expense for more discussion.
  340. */
  341. Cost xfunc_expense(clause)
  342.     LispValue clause;
  343. {
  344.     Cost cost = 0;   /* running expense */
  345.     LispValue tmpclause;
  346.  
  347.     /* First handle the base case */
  348.     if (IsA(clause,Const) || IsA(clause,Var) || IsA(clause,Param)) 
  349.       return(0);
  350.     /* now other stuff */
  351.     else if (IsA(clause,Iter))
  352.       /* Too low. Should multiply by the expected number of iterations. */
  353.       return(xfunc_expense(get_iterexpr((Iter)clause)));
  354.     else if (IsA(clause,ArrayRef))
  355.       return(xfunc_expense(get_refexpr((ArrayRef)clause)));
  356.     else if (fast_is_clause(clause)) 
  357.       return(xfunc_func_expense((LispValue)get_op(clause), 
  358.                 (LispValue)get_opargs(clause)));
  359.     else if (fast_is_funcclause(clause))
  360.       return(xfunc_func_expense((LispValue)get_function(clause),
  361.                 (LispValue)get_funcargs(clause)));
  362.     else if (fast_not_clause(clause))
  363.       return(xfunc_expense(CDR(clause)));
  364.     else if (fast_or_clause(clause))
  365.      {
  366.      /* find cost of evaluating each disjunct */
  367.      for (tmpclause = CDR(clause); tmpclause != LispNil; 
  368.           tmpclause = CDR(tmpclause))
  369.        cost += xfunc_expense(CAR(tmpclause));
  370.      return(cost);
  371.      }
  372.     else
  373.      {
  374.      elog(WARN, "Clause node of undetermined type");
  375.      return(-1);
  376.      }
  377. }
  378.  
  379. /*
  380. ** xfunc_func_expense --
  381. **    given a Func or Oper and its args, find its expense.
  382. ** Note: in Stonebraker's SIGMOD '91 paper, he uses a more complicated metric
  383. ** than the one here.  We can ignore the expected number of tuples for
  384. ** our calculations; we just need the per-tuple expense.  But he also
  385. ** proposes components to take into account the costs of accessing disk and
  386. ** archive.  We didn't adopt that scheme here; eventually the vacuum
  387. ** cleaner should be able to tell us what percentage of bytes to find on
  388. ** which storage level, and that should be multiplied in appropriately
  389. ** in the cost function below.  Right now we don't model the cost of
  390. ** accessing secondary or tertiary storage, since we don't have sufficient
  391. ** stats to do it right.
  392. */
  393. Cost xfunc_func_expense(node, args)
  394. LispValue node;
  395. LispValue args;
  396. {
  397.     HeapTuple tupl;    /* the pg_proc tuple for each function */
  398.     Form_pg_proc proc; /* a data structure to hold the pg_proc tuple */
  399.     int width = 0;     /* byte width of the field referenced by each clause */
  400.     regproc funcid;    /* ID of function associate with node */
  401.     Cost cost = 0;   /* running expense */
  402.     LispValue tmpclause;
  403.     char *globals;
  404.  
  405.     if (IsA(node,Oper))
  406.      {
  407.      /* don't trust the opid in the Oper node.  Use the opno. */
  408.      if (!(funcid = get_opcode(get_opno((Oper)node))))
  409.        elog(WARN, "Oper's function is undefined");
  410.      }
  411.     else funcid = get_funcid((Func)node);
  412.  
  413.     /* look up tuple in cache */
  414.     tupl = SearchSysCacheTuple(PROOID, funcid, NULL, NULL, NULL);
  415.     if (!HeapTupleIsValid(tupl))
  416.       elog(WARN, "Cache lookup failed for procedure %d", funcid);
  417.     proc = (Form_pg_proc) GETSTRUCT(tupl);
  418.  
  419.     /* 
  420.     ** if it's a Postquel function, its cost is stored in the
  421.     ** associated plan.
  422.     */
  423.     if (proc->prolang == POSTQUELlanguageId)
  424.      {
  425.      LispValue tmpplan;
  426.      List planlist;
  427.      
  428.      if (IsA(node,Oper) || get_func_planlist((Func)node) == LispNil)
  429.       {
  430.           ObjectId *argOidVect; /* vector of argtypes */
  431.           char *pq_src;    /* text of PQ function */
  432.           int nargs;    /* num args to PQ function */
  433.           LispValue parseTree_list;    /* dummy variable */
  434.           ObjectId *funcname_get_funcargtypes();
  435.  
  436.           /* 
  437.           ** plan the function, storing it in the Func node for later 
  438.           ** use by the executor.  
  439.           */
  440.           pq_src = (char *) textout(&(proc->prosrc));
  441.           nargs = proc->pronargs;
  442.           if (nargs > 0)
  443.         argOidVect = funcname_get_funcargtypes(&proc->proname.data[0]);
  444.           /*
  445.            * save/restore globals -
  446.            * 
  447.            * This is a hack to make pg_plan reentrant.
  448.            */
  449.           globals = save_globals();
  450.           planlist = (List)pg_plan(pq_src, argOidVect, nargs, 
  451.                        &parseTree_list, None);
  452.           restore_globals(globals);
  453.           if (IsA(node,Func))
  454.         set_func_planlist((Func)node, planlist);
  455.       }
  456.      else /* plan has been cached inside the Func node already */
  457.        planlist = get_func_planlist((Func)node);
  458.  
  459.      /*
  460.      ** Return the sum of the costs of the plans (the PQ function
  461.      ** may have many queries in its body).
  462.      */
  463.      foreach(tmpplan, planlist)
  464.        cost += get_cost((Plan)CAR(tmpplan));
  465.      return(cost);
  466.      }
  467.     else            /* it's a C function */
  468.      {
  469.      /* find cost of evaluating the function's arguments */
  470.      for (tmpclause = args; tmpclause != LispNil; 
  471.           tmpclause = CDR(tmpclause))
  472.        cost += xfunc_expense(CAR(tmpclause));
  473.  
  474.      /* find width of operands */
  475.      for (tmpclause = args; tmpclause != LispNil;
  476.           tmpclause = CDR(tmpclause))
  477.        width += xfunc_width(CAR(tmpclause));
  478.  
  479.      /* 
  480.      ** when stats become available, add in cost of accessing secondary
  481.      ** and tertiary storage here.
  482.      */
  483.      return(cost +  
  484.         (Cost)proc->propercall_cpu + 
  485.         (Cost)proc->properbyte_cpu * (Cost)proc->probyte_pct/100.00 * 
  486.         (Cost)width
  487.          /* 
  488.                    * Pct_of_obj_in_mem
  489.         DISK_COST * proc->probyte_pct/100.00 * width
  490.            * Pct_of_obj_on_disk +
  491.         ARCH_COST * proc->probyte_pct/100.00 * width
  492.            * Pct_of_obj_on_arch 
  493.          */
  494.         );
  495.      }
  496. }
  497.  
  498. /* 
  499. ** xfunc_width --
  500. **    recursively find the width of a expression
  501. */
  502.  
  503. int xfunc_width(clause)
  504.      LispValue clause;
  505. {
  506.     Relation rd;         /* Relation Descriptor */
  507.     HeapTuple tupl;      /* structure to hold a cached tuple */
  508.     Form_pg_type type;   /* structure to hold a type tuple */
  509.     int retval = 0;
  510.  
  511.     if (IsA(clause,Const))
  512.      {
  513.      /* base case: width is the width of this constant */
  514.      retval = get_constlen((Const) clause);
  515.      goto exit;
  516.      }
  517.     else if (IsA(clause,ArrayRef))
  518.      {
  519.      /* base case: width is width of the refelem within the array */
  520.      retval = get_refelemlength((ArrayRef)clause);
  521.      goto exit;
  522.      }
  523.     else if (IsA(clause,Var))  /* base case: width is width of this attribute */
  524.      {
  525.      tupl = SearchSysCacheTuple(TYPOID, get_vartype((Var)clause),
  526.                     NULL, NULL, NULL);
  527.      if (!HeapTupleIsValid(tupl))
  528.        elog(WARN, "Cache lookup failed for type %d", 
  529.         get_vartype((Var)clause));
  530.      type = (Form_pg_type) GETSTRUCT(tupl);
  531.      if (get_varattno((Var)clause) == 0)
  532.       {
  533.           /* clause is a tuple.  Get its width */
  534.           rd = heap_open(type->typrelid);
  535.           retval = xfunc_tuple_width(rd);
  536.           heap_close(rd);
  537.       }
  538.      else
  539.        /* attribute is a base type */
  540.        retval = type->typlen;
  541.      goto exit;
  542.      }
  543.     else if (IsA(clause,Param))
  544.      {
  545.      if (typeid_get_relid(get_paramtype((Param)clause)))
  546.       {
  547.           /* Param node returns a tuple.  Find its width */
  548.           rd = heap_open(typeid_get_relid(get_paramtype((Param)clause)));
  549.           retval = xfunc_tuple_width(rd);
  550.           heap_close(rd);
  551.       }
  552.      else if (get_param_tlist((Param)clause) != LispNil)
  553.       {
  554.           /* Param node projects a complex type */
  555.           Assert(length(get_param_tlist((Param)clause)) == 1); /* sanity */
  556.           retval = 
  557.         xfunc_width((LispValue)
  558.                 get_expr((TLE)CAR(get_param_tlist((Param)clause))));
  559.       }
  560.      else
  561.       {
  562.           /* Param node returns a base type */
  563.           retval = tlen(get_id_type(get_paramtype((Param)clause)));
  564.       }
  565.      goto exit;     
  566.      }
  567.     else if (IsA(clause,Iter))
  568.      {
  569.      /* 
  570.          ** An Iter returns a setof things, so return the width of a single
  571.      ** thing.
  572.      ** Note:  THIS MAY NOT WORK RIGHT WHEN AGGS GET FIXED,
  573.      ** SINCE AGG FUNCTIONS CHEW ON THE WHOLE SETOF THINGS!!!!
  574.      */
  575.      retval = xfunc_width(get_iterexpr((Iter)clause));
  576.      goto exit;
  577.      }
  578.     else if (fast_is_clause(clause))
  579.      {
  580.      /*
  581.      ** get function associated with this Oper, and treat this as 
  582.      ** a Func 
  583.      */
  584.      tupl = SearchSysCacheTuple(OPROID, get_opno((Oper)get_op(clause)),
  585.                     NULL, NULL, NULL);
  586.      if (!HeapTupleIsValid(tupl))
  587.        elog(WARN, "Cache lookup failed for procedure %d", 
  588.         get_opno((Oper)get_op(clause)));
  589.      return(xfunc_func_width
  590.         ((regproc)(((Form_pg_operator)(GETSTRUCT(tupl)))->oprcode), 
  591.          (LispValue)get_opargs(clause)));
  592.      }
  593.     else if (fast_is_funcclause(clause))
  594.      {
  595.      Func func = (Func)get_function(clause);
  596.      if (get_func_tlist(func) != LispNil)
  597.       {
  598.           /* this function has a projection on it.  Get the length
  599.          of the projected attribute */
  600.           Assert(length(get_func_tlist(func)) == 1);   /* sanity */
  601.           retval = 
  602.         xfunc_width((LispValue)
  603.                 get_expr((TLE)CAR(get_func_tlist(func))));
  604.           goto exit;
  605.       }
  606.      else
  607.       {
  608.           return(xfunc_func_width((regproc)get_funcid(func),
  609.                       (LispValue)get_funcargs(clause)));
  610.       }
  611.      }
  612.     else
  613.      {
  614.      elog(WARN, "Clause node of undetermined type");
  615.      return(-1);
  616.      }
  617.  
  618.   exit:
  619.     if (retval == -1)
  620.       retval = VARLEN_DEFAULT;
  621.     return(retval);
  622. }
  623.  
  624.  
  625. /*
  626. ** xfunc_primary_join:
  627. **   Find the primary join clause: for Hash and Merge Joins, this is the
  628. ** min measure Hash or Merge clause, while for Nested Loop it's the
  629. ** min measure pathclause
  630. */
  631. LispValue xfunc_primary_join(pathnode)
  632.      JoinPath pathnode;
  633. {
  634.     LispValue joinclauselist = get_pathclauseinfo(pathnode);
  635.     CInfo mincinfo;
  636.     LispValue tmplist;
  637.     LispValue minclause;
  638.     Cost minmeasure, tmpmeasure;
  639.  
  640.     if (IsA(pathnode,MergePath)) 
  641.      {
  642.      for(tmplist = get_path_mergeclauses((MergePath)pathnode),
  643.          minclause = CAR(tmplist),
  644.          minmeasure = xfunc_measure(minclause);
  645.          tmplist != LispNil;
  646.          tmplist = CDR(tmplist))
  647.        if ((tmpmeasure = xfunc_measure(CAR(tmplist)))
  648.            < minmeasure)
  649.         {
  650.         minmeasure = tmpmeasure;
  651.         minclause = CAR(tmplist);
  652.         }
  653.      return(minclause);
  654.      }
  655.     else if (IsA(pathnode,HashPath)) 
  656.      {
  657.      for(tmplist = get_path_hashclauses((HashPath)pathnode),
  658.          minclause = CAR(tmplist),
  659.          minmeasure = xfunc_measure(minclause);
  660.          tmplist != LispNil;
  661.          tmplist = CDR(tmplist))
  662.        if ((tmpmeasure = xfunc_measure(CAR(tmplist)))
  663.            < minmeasure)
  664.         {
  665.         minmeasure = tmpmeasure;
  666.         minclause = CAR(tmplist);
  667.         }
  668.      return(minclause);
  669.      }
  670.  
  671.     /* if we drop through, it's nested loop join */
  672.     if (joinclauselist == LispNil)
  673.       return(LispNil);
  674.  
  675.     for(tmplist = joinclauselist, mincinfo = (CInfo) CAR(joinclauselist),
  676.     minmeasure = xfunc_measure(get_clause((CInfo) CAR(tmplist)));
  677.     tmplist != LispNil;
  678.     tmplist = CDR(tmplist))
  679.       if ((tmpmeasure = xfunc_measure(get_clause((CInfo) CAR(tmplist))))
  680.       < minmeasure)
  681.        {
  682.        minmeasure = tmpmeasure;
  683.        mincinfo = (CInfo) CAR(tmplist);
  684.        }
  685.     return((LispValue)get_clause(mincinfo));
  686. }
  687.  
  688. /*
  689. ** xfunc_get_path_cost
  690. **   get the expensive function costs of the path
  691. */
  692. Cost xfunc_get_path_cost(pathnode)
  693. Path pathnode;
  694. {
  695.     Cost cost = 0;
  696.     LispValue tmplist;
  697.     Cost selec = 1.0;
  698.     
  699.     /* 
  700.     ** first add in the expensive local function costs.
  701.     ** We ensure that the clauses are sorted by measure, so that we
  702.     ** know (via selectivities) the number of tuples that will be checked
  703.     ** by each function.  If we're not doing any optimization of expensive
  704.     ** functions, we don't sort.
  705.     */
  706.     if (XfuncMode != XFUNC_OFF)
  707.       set_locclauseinfo(pathnode, lisp_qsort(get_locclauseinfo(pathnode),
  708.                          xfunc_cinfo_compare));
  709.     for(tmplist = get_locclauseinfo(pathnode), selec = 1.0;
  710.     tmplist != LispNil;
  711.     tmplist = CDR(tmplist))
  712.      {
  713.      cost += (Cost)(xfunc_expense(get_clause((CInfo)CAR(tmplist)))
  714.             * (Cost)get_tuples(get_parent(pathnode)) * selec);
  715.      selec *= compute_clause_selec(get_clause((CInfo)CAR(tmplist)), 
  716.                        LispNil);
  717.      }
  718.  
  719.     /* 
  720.     ** Now add in any node-specific expensive function costs.
  721.     ** Again, we must ensure that the clauses are sorted by measure.
  722.     */
  723.     if (IsA(pathnode,JoinPath))
  724.      {
  725.      if (XfuncMode != XFUNC_OFF)
  726.        set_pathclauseinfo((JoinPath)pathnode, lisp_qsort
  727.                   (get_pathclauseinfo((JoinPath)pathnode),
  728.                    xfunc_cinfo_compare));
  729.      for(tmplist = get_pathclauseinfo((JoinPath)pathnode), selec = 1.0;
  730.          tmplist != LispNil;
  731.          tmplist = CDR(tmplist))
  732.       {
  733.           cost += (Cost)(xfunc_expense(get_clause((CInfo)CAR(tmplist)))
  734.                  * (Cost)get_tuples(get_parent(pathnode)) * selec);
  735.           selec *= compute_clause_selec(get_clause((CInfo)CAR(tmplist)),
  736.                         LispNil);
  737.       }
  738.      }
  739.     if (IsA(pathnode,HashPath))
  740.      {
  741.      if (XfuncMode != XFUNC_OFF)
  742.        set_path_hashclauses
  743.          ((HashPath)pathnode, 
  744.           lisp_qsort(get_path_hashclauses((HashPath)pathnode),
  745.              xfunc_clause_compare));
  746.      for(tmplist = get_path_hashclauses((HashPath)pathnode), selec = 1.0;
  747.          tmplist != LispNil;
  748.          tmplist = CDR(tmplist))
  749.       {
  750.           cost += (Cost)(xfunc_expense(CAR(tmplist))
  751.                  * (Cost)get_tuples(get_parent(pathnode)) * selec);
  752.           selec *= compute_clause_selec(CAR(tmplist), LispNil);
  753.       }
  754.      }
  755.     if (IsA(pathnode,MergePath))
  756.      {
  757.      if (XfuncMode != XFUNC_OFF)
  758.        set_path_mergeclauses
  759.          ((MergePath)pathnode, 
  760.           lisp_qsort(get_path_mergeclauses((MergePath)pathnode),
  761.              xfunc_clause_compare));
  762.      for(tmplist = get_path_mergeclauses((MergePath)pathnode), selec = 1.0;
  763.          tmplist != LispNil;
  764.          tmplist = CDR(tmplist))
  765.       {
  766.           cost += (Cost)(xfunc_expense(CAR(tmplist))
  767.                  * (Cost)get_tuples(get_parent(pathnode)) * selec);
  768.           selec *= compute_clause_selec(CAR(tmplist), LispNil);
  769.       }
  770.      }
  771.     Assert(cost >= 0);
  772.     return(cost);
  773. }
  774.  
  775. /*
  776. ** Recalculate the cost of a path node.  This includes the basic cost of the 
  777. ** node, as well as the cost of its expensive functions.
  778. ** We need to do this to the parent after pulling a clause from a child into a
  779. ** parent.  Thus we should only be calling this function on JoinPaths.
  780. */
  781. Cost xfunc_total_path_cost(pathnode)
  782. JoinPath pathnode;
  783. {
  784.     Cost cost = xfunc_get_path_cost((Path)pathnode);
  785.  
  786.     Assert(IsA(pathnode,JoinPath));
  787.     if (IsA(pathnode,MergePath))
  788.      {
  789.      MergePath mrgnode = (MergePath)pathnode;
  790.      cost += cost_mergesort(get_path_cost((Path)get_outerjoinpath(mrgnode)),
  791.                 get_path_cost((Path)get_innerjoinpath(mrgnode)),
  792.                 get_outersortkeys(mrgnode),
  793.                 get_innersortkeys(mrgnode),
  794.                 get_tuples(get_parent((Path)get_outerjoinpath
  795.                               (mrgnode))),
  796.                 get_tuples(get_parent((Path)get_innerjoinpath
  797.                               (mrgnode))),
  798.                 get_width(get_parent((Path)get_outerjoinpath
  799.                              (mrgnode))),
  800.                 get_width(get_parent((Path)get_innerjoinpath
  801.                              (mrgnode))));
  802.      Assert(cost >= 0);
  803.      return(cost);
  804.      }
  805.     else if (IsA(pathnode,HashPath))
  806.      {
  807.      HashPath hashnode = (HashPath)pathnode;
  808.      cost += cost_hashjoin(get_path_cost((Path)get_outerjoinpath(hashnode)),
  809.                    get_path_cost((Path)get_innerjoinpath(hashnode)),
  810.                    get_outerhashkeys(hashnode),
  811.                    get_innerhashkeys(hashnode),
  812.                    get_tuples(get_parent((Path)get_outerjoinpath
  813.                              (hashnode))),
  814.                    get_tuples(get_parent((Path)get_innerjoinpath
  815.                              (hashnode))),
  816.                    get_width(get_parent((Path)get_outerjoinpath
  817.                             (hashnode))),
  818.                    get_width(get_parent((Path)get_innerjoinpath
  819.                             (hashnode))));
  820.      Assert (cost >= 0);
  821.      return(cost);
  822.      }
  823.     else /* Nested Loop Join */
  824.      {
  825.      cost += cost_nestloop(get_path_cost((Path)get_outerjoinpath(pathnode)),
  826.                    get_path_cost((Path)get_innerjoinpath(pathnode)),
  827.                    get_tuples(get_parent((Path)get_outerjoinpath
  828.                              (pathnode))),
  829.                    get_tuples(get_parent((Path)get_innerjoinpath
  830.                              (pathnode))),
  831.                    get_pages(get_parent((Path)get_outerjoinpath
  832.                             (pathnode))),
  833.                    IsA(get_innerjoinpath(pathnode),IndexPath));
  834.      Assert(cost >= 0);
  835.      return(cost);
  836.      }
  837. }
  838.  
  839.  
  840. /*
  841. ** xfunc_expense_per_tuple --
  842. **    return the expense of the join *per-tuple* of the input relation.
  843. ** This will be different for tuples of INNER and OUTER
  844. */
  845. Cost xfunc_expense_per_tuple(joinnode, whichrel)
  846. JoinPath joinnode;
  847. int whichrel;       /* INNER or OUTER of joinnode */
  848. {
  849.     int outersize = get_tuples(get_parent((Path)get_outerjoinpath(joinnode)));
  850.     int innersize = get_tuples(get_parent((Path)get_innerjoinpath(joinnode)));
  851.  
  852.     /* for merge join, assume just cost of other side */
  853.     if (IsA(joinnode,MergePath))
  854.      {
  855.      /*
  856.       * kai: care for zero innersize / outersize here!
  857.       */
  858.      if (whichrel == INNER)
  859.          if(outersize > 0)
  860.          return(_CPU_PAGE_WEIGHT_ * outersize * log(outersize));
  861.          else
  862.          return (Cost) 0;
  863.      if(innersize > 0)
  864.          return(_CPU_PAGE_WEIGHT_ * innersize * log(innersize));
  865.      else
  866.          return (Cost) 0;
  867.      }
  868.     /* 
  869.     ** For hash join, figure out the number of chunks of the outer we process
  870.     ** at a time.  The cost for a tuple of the outer is the CPU cost
  871.     ** times the size of the inner relation, and for a tuple of the inner
  872.     ** it's the CPU cost times the number of chunks of the outer 
  873.     */
  874.     else if (IsA(joinnode,HashPath))
  875.      {
  876.      int outerpages = 
  877.        get_pages(get_parent((Path)get_outerjoinpath(joinnode)));
  878.  
  879.      int nrun = ceil((double)outerpages/(double)NBuffers);
  880.      if (whichrel == INNER)
  881.        return((Cost)(_CPU_PAGE_WEIGHT_ * nrun));
  882.      else
  883.        return((Cost)(_CPU_PAGE_WEIGHT_ * innersize));
  884.      }
  885.     /*
  886.     ** For nested loop, the cost for tuples is the size of the outer relation
  887.     */
  888.     else /* nested loop join */
  889.       if (whichrel == INNER)
  890.     return((Cost)(outersize * _CPU_PAGE_WEIGHT_));
  891.       else
  892.     return((Cost)(innersize * _CPU_PAGE_WEIGHT_));
  893. }
  894.  
  895. /*
  896. ** xfunc_fixvars --
  897. ** After pulling up a clause, we must walk its expression tree, fixing Var 
  898. ** nodes to point to the correct varno (either INNER or OUTER, depending
  899. ** on which child the clause was pulled from), and the right varattno in the 
  900. ** target list of the child's former relation.  If the target list of the
  901. ** child Rel does not contain the attribute we need, we add it.
  902. */
  903. void xfunc_fixvars(clause, rel, varno)
  904.      LispValue clause;  /* clause being pulled up */
  905.      Rel rel;           /* rel it's being pulled from */
  906.      int varno;         /* whether rel is INNER or OUTER of join */
  907. {
  908.     LispValue tmpclause;  /* temporary variable */
  909.     TLE tle;              /* tlist member corresponding to var */
  910.  
  911.     if (IsA(clause,Const) || IsA(clause,Param)) return;
  912.     else if (IsA(clause,Var))
  913.      {
  914.      /* here's the meat */
  915.      tle = tlistentry_member((Var)clause, get_targetlist(rel));
  916.      if (tle == LispNil)
  917.       {
  918.           /* 
  919.            ** The attribute we need is not in the target list,
  920.            ** so we have to add it.
  921.            **
  922.            ** Note: the last argument to add_tl_element() may be wrong,
  923.            ** but it doesn't seem like anybody uses this field
  924.            ** anyway.  It's definitely supposed to be a list of relids,
  925.            ** so I just figured I'd use the ones in the clause.
  926.            */
  927.           add_tl_element(rel, (Var)clause, clause_relids_vars(clause));
  928.           tle = tlistentry_member((Var)clause, get_targetlist(rel));
  929.       }
  930.      ((Var)clause)->varno = varno;
  931.      ((Var)clause)->varattno = get_resno(get_resdom(get_entry(tle)));
  932.      }
  933.     else if (IsA(clause,Iter))
  934.       xfunc_fixvars(get_iterexpr((Iter)clause), rel, varno);
  935.     else if (fast_is_clause(clause))
  936.      {
  937.      xfunc_fixvars(CAR(CDR(clause)), rel, varno);
  938.      xfunc_fixvars(CAR(CDR(CDR(clause))), rel, varno);
  939.      }
  940.     else if (fast_is_funcclause(clause))
  941.       for (tmpclause = CDR(clause); tmpclause != LispNil; 
  942.        tmpclause = CDR(tmpclause))
  943.     xfunc_fixvars(CAR(tmpclause), rel, varno);
  944.     else if (fast_not_clause(clause))
  945.       xfunc_fixvars(CDR(clause), rel, varno);
  946.     else if (fast_or_clause(clause))
  947.       for (tmpclause = CDR(clause); tmpclause != LispNil; 
  948.        tmpclause = CDR(tmpclause))
  949.     xfunc_fixvars(CAR(tmpclause), rel, varno);
  950.     else
  951.      {
  952.      elog(WARN, "Clause node of undetermined type");
  953.      }
  954. }
  955.  
  956.  
  957. /*
  958. ** Comparison function for lisp_qsort() on a list of CInfo's.
  959. ** arg1 and arg2 should really be of type (CInfo *).  
  960. */
  961. int xfunc_cinfo_compare(arg1, arg2)
  962.     void *arg1;
  963.     void *arg2;
  964. {
  965.     CInfo info1 = *(CInfo *) arg1;
  966.     CInfo info2 = *(CInfo *) arg2;
  967.  
  968.     LispValue clause1 = (LispValue) get_clause(info1),
  969.               clause2 = (LispValue) get_clause(info2);
  970.     
  971.     return(xfunc_clause_compare((void *) &clause1, (void *) &clause2));
  972. }
  973.  
  974. /*
  975. ** xfunc_clause_compare: comparison function for lisp_qsort() that compares two 
  976. ** clauses based on expense/(1 - selectivity)
  977. ** arg1 and arg2 are really pointers to clauses.
  978. */
  979. int xfunc_clause_compare(arg1, arg2)
  980.     void *arg1;
  981.     void *arg2;
  982. {
  983.     LispValue clause1 = *(LispValue *) arg1;
  984.     LispValue clause2 = *(LispValue *) arg2;
  985.     Cost measure1,             /* total xfunc measure of clause1 */ 
  986.            measure2;             /* total xfunc measure of clause2 */
  987.     int infty1 = 0, infty2 = 0;  /* divide by zero is like infinity */
  988.  
  989.     measure1 = xfunc_measure(clause1);
  990.     if (measure1 == -1.0) infty1 = 1;
  991.     measure2 = xfunc_measure(clause2);
  992.     if (measure2 == -1.0) infty2 = 1;
  993.  
  994.     if (infty1 || infty2)
  995.      {
  996.      if (!infty1) return(-1);
  997.      else if (infty1 && infty2) return(0);
  998.      else return(1);
  999.      }
  1000.     
  1001.     if ( measure1 < measure2) 
  1002.       return(-1);
  1003.     else if (measure1 == measure2)
  1004.       return(0);
  1005.     else return(1);
  1006. }
  1007.  
  1008. /*
  1009. ** xfunc_disjunct_sort --
  1010. **   given a list of clauses, for each clause sort the disjuncts by cost
  1011. **   (this assumes the predicates have been converted to Conjunctive NF)
  1012. **   Modifies the clause list!
  1013. */
  1014. void xfunc_disjunct_sort(clause_list)
  1015.     LispValue clause_list;
  1016. {
  1017.     LispValue temp;
  1018.  
  1019.     foreach(temp, clause_list)
  1020.       if(or_clause(CAR(temp)))
  1021.     CDR(CAR(temp)) = lisp_qsort(CDR(CAR(temp)), xfunc_disjunct_compare);
  1022. }
  1023.  
  1024.  
  1025. /*
  1026. ** xfunc_disjunct_compare: comparison function for qsort() that compares two 
  1027. ** disjuncts based on expense.
  1028. ** arg1 and arg2 are really pointers to disjuncts
  1029. */
  1030. int xfunc_disjunct_compare(arg1, arg2)
  1031.     void *arg1;
  1032.     void *arg2;
  1033. {
  1034.     LispValue disjunct1 = *(LispValue *) arg1;
  1035.     LispValue disjunct2 = *(LispValue *) arg2;
  1036.     Cost measure1,  /* total cost of disjunct1 */ 
  1037.            measure2;  /* total cost of disjunct2 */
  1038.  
  1039.     measure1 = xfunc_expense(disjunct1);
  1040.     measure2 = xfunc_expense(disjunct2);
  1041.     
  1042.     if ( measure1 < measure2) 
  1043.       return(-1);
  1044.     else if (measure1 == measure2)
  1045.       return(0);
  1046.     else return(1);
  1047. }
  1048.  
  1049. /* ------------------------ UTILITY FUNCTIONS ------------------------------- */
  1050. /*
  1051. ** xfunc_func_width --
  1052. **    Given a function OID and operands, find the width of the return value.
  1053. */
  1054. int xfunc_func_width(funcid, args)
  1055. regproc funcid;
  1056. LispValue args;
  1057. {
  1058.     Relation rd;         /* Relation Descriptor */
  1059.     HeapTuple tupl;      /* structure to hold a cached tuple */
  1060.     Form_pg_proc proc;   /* structure to hold the pg_proc tuple */
  1061.     Form_pg_type type;   /* structure to hold the pg_type tuple */
  1062.     LispValue tmpclause; 
  1063.     int retval;
  1064.  
  1065.     /* lookup function and find its return type */
  1066.     Assert(RegProcedureIsValid(funcid));
  1067.     tupl = SearchSysCacheTuple(PROOID, funcid, NULL, NULL, NULL);
  1068.     if (!HeapTupleIsValid(tupl))
  1069.       elog(WARN, "Cache lookup failed for procedure %d", funcid);
  1070.     proc = (Form_pg_proc) GETSTRUCT(tupl);
  1071.     
  1072.     /* if function returns a tuple, get the width of that */
  1073.     if (typeid_get_relid(proc->prorettype))
  1074.      {
  1075.      rd = heap_open(typeid_get_relid(proc->prorettype));
  1076.      retval = xfunc_tuple_width(rd);
  1077.      heap_close(rd);
  1078.      goto exit;
  1079.      }
  1080.     else /* function returns a base type */
  1081.      {
  1082.      tupl = SearchSysCacheTuple(TYPOID,
  1083.                     proc->prorettype,
  1084.                     NULL, NULL, NULL);
  1085.      if (!HeapTupleIsValid(tupl))
  1086.        elog(WARN, "Cache lookup failed for type %d", proc->prorettype);
  1087.      type = (Form_pg_type) GETSTRUCT(tupl);
  1088.      /* if the type length is known, return that */
  1089.      if (type->typlen != -1)
  1090.       {
  1091.           retval = type->typlen;
  1092.           goto exit;
  1093.       }
  1094.      else /* estimate the return size */
  1095.       {
  1096.           /* find width of the function's arguments */
  1097.           for (tmpclause = args; tmpclause != LispNil; 
  1098.            tmpclause = CDR(tmpclause))
  1099.         retval += xfunc_width(CAR(tmpclause));
  1100.           /* multiply by outin_ratio */
  1101.           retval = (int)(proc->prooutin_ratio/100.0 * retval);
  1102.           goto exit;
  1103.       }
  1104.      }
  1105.   exit:
  1106.     return(retval);
  1107. }
  1108.  
  1109. /*
  1110. ** xfunc_tuple_width --
  1111. **     Return the sum of the lengths of all the attributes of a given relation
  1112. */
  1113. int xfunc_tuple_width(rd)
  1114.      Relation rd;
  1115. {
  1116.     int i;
  1117.     int retval = 0;
  1118.     TupleDescriptor tdesc = RelationGetTupleDescriptor(rd);
  1119.     Assert(TupleDescIsValid(tdesc));
  1120.  
  1121.     for (i = 0; i < RelationGetNumberOfAttributes(rd); i++)
  1122.      {
  1123.      if (tdesc->data[i]->attlen != -1)
  1124.        retval += tdesc->data[i]->attlen;
  1125.      else retval += VARLEN_DEFAULT;
  1126.      }
  1127.  
  1128.     return(retval);
  1129. }
  1130.  
  1131. /*
  1132. ** xfunc_num_join_clauses --
  1133. **   Find the number of join clauses associated with this join path
  1134. */
  1135. int xfunc_num_join_clauses(path)
  1136. JoinPath path;
  1137. {
  1138.     int num = length(get_pathclauseinfo(path));
  1139.     if (IsA(path,MergePath))
  1140.       return(num + length(get_path_mergeclauses((MergePath)path)));
  1141.     else if (IsA(path,HashPath))
  1142.       return(num + length(get_path_hashclauses((HashPath)path)));
  1143.     else return(num);
  1144. }
  1145.  
  1146. /*
  1147. ** xfunc_LispRemove --
  1148. **   Just like LispRemove, but it whines if the item to be removed ain't there
  1149. */
  1150. LispValue xfunc_LispRemove(foo, bar)
  1151.      LispValue foo;
  1152.      List bar;
  1153. {
  1154.     LispValue temp = LispNil;
  1155.     LispValue result = LispNil;
  1156.     int sanity = false;
  1157.  
  1158.     for (temp = bar; !null(temp); temp = CDR(temp))
  1159.       if (! equal((Node)(foo),(Node)(CAR(temp))) ) 
  1160.        {
  1161.        result = append1(result,CAR(temp));
  1162.        }
  1163.       else sanity = true; /* found a matching item to remove! */
  1164.  
  1165.     if (!sanity)
  1166.       elog(WARN, "xfunc_LispRemove: didn't find a match!");
  1167.  
  1168.     return(result);
  1169. }
  1170.  
  1171. #define Node_Copy(a, b, c, d) \
  1172.     if (NodeCopy(((a)->d), &((b)->d), c) != true) { \
  1173.        return false; \
  1174.     } 
  1175.  
  1176. /*
  1177. ** xfunc_copyrel --
  1178. **   Just like _copyRel, but doesn't copy the paths
  1179. */
  1180. bool
  1181. xfunc_copyrel(from, to)
  1182.     Rel    from;
  1183.     Rel    *to;
  1184. {
  1185.     Rel    newnode;
  1186.     Pointer (*alloc)() = palloc;
  1187.  
  1188.     /*  COPY_CHECKARGS() */
  1189.     if (to == NULL) 
  1190.      { 
  1191.      return false;
  1192.      } 
  1193.                       
  1194.     /* COPY_CHECKNULL() */
  1195.     if (from == NULL) 
  1196.      {
  1197.      (*to) = NULL;
  1198.      return true;
  1199.      } 
  1200.  
  1201.     /* COPY_NEW(c) */
  1202.     newnode =  (Rel)(*alloc)(classSize(Rel));
  1203.     if (newnode == NULL) 
  1204.      { 
  1205.      return false;
  1206.      } 
  1207.     
  1208.     /* ----------------
  1209.      *    copy node superclass fields
  1210.      * ----------------
  1211.      */
  1212.     CopyNodeFields(from, newnode, alloc);
  1213.  
  1214.     /* ----------------
  1215.      *    copy remainder of node
  1216.      * ----------------
  1217.      */
  1218.     Node_Copy(from, newnode, alloc, relids);
  1219.     
  1220.     newnode->indexed = from->indexed;
  1221.     newnode->pages =   from->pages;
  1222.     newnode->tuples =  from->tuples;
  1223.     newnode->size =    from->size;
  1224.     newnode->width =   from->width;
  1225.  
  1226.     Node_Copy(from, newnode, alloc, targetlist);
  1227. /* No!!!!    Node_Copy(from, newnode, alloc, pathlist);  
  1228.     Node_Copy(from, newnode, alloc, unorderedpath);
  1229.     Node_Copy(from, newnode, alloc, cheapestpath);  */
  1230.     Node_Copy(from, newnode, alloc, classlist);
  1231.     Node_Copy(from, newnode, alloc, indexkeys);
  1232.     Node_Copy(from, newnode, alloc, ordering);
  1233.     Node_Copy(from, newnode, alloc, clauseinfo);
  1234.     Node_Copy(from, newnode, alloc, joininfo);
  1235.     Node_Copy(from, newnode, alloc, innerjoin);
  1236.     Node_Copy(from, newnode, alloc, superrels);
  1237.     
  1238.     (*to) = newnode;
  1239.     return true;
  1240. }
  1241.